package parser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class FloorPlanParser {

	String line = null;
	String[] lineParts;
	FileReader reader;
	BufferedReader buff;
	ParseResult result = null;
	
	public ParseResult getResult() {
		return result;
	}

	FloorPlan floorPlan = null;
	public Map<String, Block> blocks;
	private Map<String, Net> nets;
	private Map<String, InterfacePin> interfacePins;

	/* Capacitance coefficients */
	private double c_vertical;
	private double c_horizontal;

	public FloorPlanParser(File parsingFile) throws IOException {
		result = parseFloorPlanFile(parsingFile);
	}

	private ParseResult parseFloorPlanFile(File parsingFile) throws IOException {
		/* Assuming the input file format is leagal */

		if (parsingFile == null)
			return ParseResult.PARSE_FILE_NOT_FOUND;

		reader = new FileReader(parsingFile);
		buff = new BufferedReader(reader);

		while ((line = buff.readLine()) != null) {
			line = line.replaceAll("\\s+", " ");
			lineParts = line.trim().split(" ");
			if (lineParts[0].compareTo(".BLOCKS") == 0)
				parseBlocksParameters();

			if (lineParts[0].compareTo(".NETS") == 0) {
				ParseResult result = parseNetsParameters();
				if (result != ParseResult.PARSE_SUCCESS)
					return ParseResult.PARSE_FILE_BAD_FORMAT;
			}

			if (lineParts[0].compareTo(".IFS") == 0)
				parseInterfacePins();

			if (lineParts[0].compareTo(".TECH") == 0)
				parseCapacitanceCo();

		}// END-WHILE

		floorPlan = new FloorPlan(blocks, nets, interfacePins, c_vertical,
				c_horizontal);

		return ParseResult.PARSE_SUCCESS;

	}

	private void parseCapacitanceCo() throws IOException {
		while ((line = buff.readLine()) != null) {
			line = line.replaceAll("\\s+", " ");
			lineParts = line.trim().split(" ");
			if (lineParts[0].compareTo(".ENDTECH") == 0)
				break;

			if (lineParts[0].compareTo(".CVERT") == 0)
				c_vertical = Double.parseDouble(lineParts[1]);

			if (lineParts[0].compareTo(".CHOR") == 0)
				c_horizontal = Double.parseDouble(lineParts[1]);
		}

	}

	private void parseInterfacePins() throws IOException {
		interfacePins = new HashMap<String, InterfacePin>();
		InterfacePin pin;

		while ((line = buff.readLine()) != null) {
			line = line.replaceAll("\\s+", " ");
			lineParts = line.trim().split(" ");
			if (lineParts[0].compareTo(".ENDIFS") == 0)
				break;
			String[] tempBuff = lineParts[1].trim().split(",");

			pin = new InterfacePin(lineParts[0],
					Double.parseDouble(tempBuff[0]),
					Double.parseDouble(tempBuff[1]));
			interfacePins.put(lineParts[0], pin);
		}

	}

	private ParseResult parseNetsParameters() throws IOException {

		nets = new HashMap<String, Net>();
		Net net;

		while ((line = buff.readLine()) != null) {
			line = line.replaceAll("\\s+", " ");
			lineParts = line.trim().split(" ");
			if (lineParts[0].compareTo(".ENDNETS") == 0)
				break;

			net = new Net();
			net.setNetName(lineParts[1]);

			while ((line = buff.readLine()) != null) {
				line = line.replaceAll("\\s+", " ");
				lineParts = line.trim().split(" ");
				if (lineParts[0].compareTo(".ENDNET") == 0)
					break;

				if (lineParts[0].compareTo(".PINS") == 0) {
					String[] tempBuff = lineParts[1].trim().split(",");
					int i = 0;
					for (i = 0; i < tempBuff.length; i++) {
						String[] nextPin = tempBuff[i].trim().split("%");
						if (nextPin.length == 2) {
							ParseResult result = insertIoPintoNet(
									net.getIo_net_pins(), nextPin[0],
									nextPin[1]);

							if (result != ParseResult.PARSE_SUCCESS)
								return ParseResult.PARSE_FILE_BAD_FORMAT;
						}

						else
							insertInterfacePinToNet(
									net.getInterface_net_pins(), nextPin[0]);
					}
				}

				if (lineParts[0].compareTo(".AF") == 0)
					net.setActivity_factor(Double.parseDouble(lineParts[1]));

				if (lineParts[0].compareTo(".BB") == 0) {
					String[] tempBuff = lineParts[1].trim().split(",");
					net.setHor_bb(Double.parseDouble(tempBuff[0]));
					net.setVertical_bb(Double.parseDouble(tempBuff[1]));
				}
			}

			nets.put(net.getNetName(), net);
		}
		return ParseResult.PARSE_SUCCESS;
	}

	private ParseResult insertInterfacePinToNet(
			Map<String, InterfacePin> netInterfacePins, String pinName) {

		InterfacePin pin = interfacePins.get(pinName);
		if (pin == null)
			return ParseResult.PARSE_FILE_BAD_FORMAT;

		netInterfacePins.put(pin.getPinName(), pin);

		return ParseResult.PARSE_SUCCESS;
	}

	private ParseResult insertIoPintoNet(Map<String, IOPin> ioPins,
			String blockName, String pinName) {

		Block block = blocks.get(blockName);
		if (block == null)
			return ParseResult.PARSE_FILE_BAD_FORMAT;

		IOPin pin = block.getPinsList().get(blockName + pinName);
		if (pin == null)
			return ParseResult.PARSE_FILE_BAD_FORMAT;

		ioPins.put(blockName + pinName, pin);

		return ParseResult.PARSE_SUCCESS;
	}

	private void parseBlocksParameters() throws IOException {
		blocks = new HashMap<String, Block>();
		Block block;

		while ((line = buff.readLine()) != null) {
			line = line.replaceAll("\\s+", " ");
			lineParts = line.trim().split(" ");
			if (lineParts[0].compareTo(".ENDBLOCKS") == 0)
				break;
			block = new Block();
			block.setBlockName(lineParts[1]);

			while ((line = buff.readLine()) != null) {

				line = line.replaceAll("\\s+", " ");
				lineParts = line.trim().split(" ");
				if (lineParts[0].compareTo(".ENDBLOCK") == 0)
					break;

				if (lineParts[0].compareTo(".CENTER") == 0) {
					String[] tempBuff = lineParts[1].trim().split(",");
					block.setX_coordinate(Double.parseDouble(tempBuff[0]));
					block.setY_coordinate(Double.parseDouble(tempBuff[1]));
				}

				if (lineParts[0].compareTo(".DIMS") == 0) {
					String[] tempBuff = lineParts[1].trim().split(",");
					block.setHorizontalLength(Double.parseDouble(tempBuff[0]));
					block.setVerticalLength(Double.parseDouble(tempBuff[1]));
				}

				if (lineParts[0].compareTo(".POWER") == 0) {
					String[] tempBuff = lineParts[1].trim().split(",");
					block.setCo_aBlock(Double.parseDouble(tempBuff[0]));
					block.setCo_bBlock(Double.parseDouble(tempBuff[1]));
					block.setCo_cBlock(Double.parseDouble(tempBuff[2]));
				}

				if (lineParts[0].compareTo(".PINS") == 0)
					parseBlockPins(block);
			}

			blocks.put(block.getBlockName(), block);
		}
	}

	private void parseBlockPins(Block block) throws IOException {
		// TODO - check input_output flag, it is not given in the parsed file.
		Map<String, IOPin> pins = new HashMap<String, IOPin>();
		IOPin pin;

		while ((line = buff.readLine()) != null) {
			line = line.replaceAll("\\s+", " ");
			lineParts = line.trim().split(" ");
			if (lineParts[0].compareTo(".ENDPINS") == 0)
				break;
			String[] tempBuff = lineParts[1].trim().split(",");
			pin = new IOPin(
					(block.getBlockName() + lineParts[0]),
					(Double.parseDouble(tempBuff[0]) - block.getX_coordinate()),
					(Double.parseDouble(tempBuff[1]) - block.getY_coordinate()),
					true);
			pins.put(pin.getPinName(), pin);
		}

		block.setPinsList(pins);
	}

	public FloorPlan getFloorPlan() {
		return this.floorPlan;
	}
}
